package com.post.subscribe;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.json.JSONException;
import org.json.JSONObject;

import com.google.gson.Gson;
import com.post.subscribe.utils.Reflect;
/**
 * Post A Object To Anywhere(Other class,Other Thread,Other Process, Other languages such as C/C#/Java)
 * @author GeLiang
 * @version 1.0.0
 * @date 2016/09/19 17:33
 */
public class Poster {
	protected static Poster instance = new Poster();
	public static final String DEFAULT_METHOD_NAME = "onPost";
	public static final String DEFAULT_METHOD_NAME_IN_MAIN = "onPostInMain";
	public static final String DEFAULT_METHOD_NAME_IN_GL = "onPostInGL";
	public final static int ThreadType_CURRENT = 0;
	public final static int ThreadType_MAIN = 1;
	public final static int ThreadType_GL = 2;
	protected List<Subscirber> subscribers = new CopyOnWriteArrayList<>();

	protected Poster() {
	}

	public static Poster getDeafult() {
		return instance;
	}


	public void regist(Object object) {
		final List<Method> methodsStartWith = Reflect.on(object)
				.getMethodsStartWith(DEFAULT_METHOD_NAME);
		if (methodsStartWith==null||methodsStartWith.size()<=0) {
			new RuntimeException("Not find a method that the name start with '"+DEFAULT_METHOD_NAME+"|"+DEFAULT_METHOD_NAME_IN_GL+"|"+DEFAULT_METHOD_NAME_IN_MAIN+"' in this Subscriber:" +object).printStackTrace();
		}
		for (Method method : methodsStartWith) {
			subscribers.add(new Subscirber(object, method));
		}
	}

	public void unregist(Object object) {
		for (Subscirber item : subscribers) {
			if (item.object ==object) {
				subscribers.remove(item);
			}
		}
	}
	/**
	 * object to JSON
	 * @param object
	 * @return
	 */
	public static String parcelToJson(Object object) {
		JSONObject jo = new JSONObject();
		final Class<? extends Object> class1 = object.getClass();
		try {
			jo.put("class", class1.getCanonicalName());
			jo.put("parcel", new JSONObject(new Gson().toJson(object)));
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return jo.toString();
	}
	/**
	 * JSON to object
	 * @param json
	 * @return
	 */
	public static Object unparcelFromJson(String json) {
		JSONObject jo;
		try {
			jo = new JSONObject(json);
			String className = jo.getString("class");
			final Object fromJson = new Gson().fromJson(jo.getString("parcel"),
					Class.forName(className));
			return fromJson;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public void post(String json) {
		post(unparcelFromJson(json));
	}

	public void post(Object obj) {
		if (obj==null) {
			return;
		}
		try {
			final String tragetName = obj.getClass().getName();
			boolean isMatch = false;
			for (Subscirber item : subscribers) {
				if (item.classParameter.getName().equals(tragetName)) {
					isMatch = true;
					switch (item.threadType) {
					case Poster.ThreadType_MAIN:
						postInMain(obj, item);
						break;
					case Poster.ThreadType_GL:
						postInGL(obj, item);
						break;
					default:
						postInCurrent(obj, item);
						break;
					}
				}
			}
			if (!isMatch) {
				throw new RuntimeException("Not find a Subscirber to recv:" +obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void postInCurrent(Object obj, Subscirber item)
			throws IllegalAccessException, InvocationTargetException {
		item.method.invoke(item.object, obj);
	}

	protected void postInMain(Object obj, Subscirber item)
			throws IllegalAccessException, InvocationTargetException {
		throw new RuntimeException("Not Support postInMain Thread");
	}

	protected void postInGL(Object obj, Subscirber item)
			throws IllegalAccessException, InvocationTargetException {
		throw new RuntimeException("Not Support postInGL Thread");
	}
}
